#define _J_DRETVE_C_
#include "dretve.h"

#include <dretve.h>
#include <pozivi_jezgre.h>

#include "procesi.h"
#include "spremnik.h"
#include "red.h"
#include "vrijeme.h"
#include "razno.h"
#include "liste.h"
#include "postavke.h"
#include "ispis.h"
#include <arh/kontekst.h>

/******************************************************************************/
/*	>>>>>>>>	Podaci jezgre (vecina)		>>>>>>>>	      */
/******************************************************************************/

//stogovi za dretve - definirano u sucelje/kod/dretve.c
extern int j_stog_dretve[][VELICINA_STOGA / sizeof(int)];

//stog za jezgrine funkcije (kada se zovu iz prekida)
int j_stogjf[VELICINA_STOGA / sizeof(int)];
void *j_prekidni_stog = POCETAK_STOGA(j_stogjf, VELICINA_STOGA/sizeof(int));

//OPISNICI dretvi
ZAUZMI(j_dretva_t, j_dretva, NAJ_DRETVI);

//pokazivac na opisnik AKTIVNE dretve
static j_dretva_t *j_aktivna = NULL;

//lista PRIPRAVNIH dretvi (struktura)
j_lista_dretvi_t j_pripravne_dretve[BRRAZPRI];
uint32 mj_pripravne_dretve[VELMASKE(BRRAZPRI)] = {0};

/******************************************************************************/
/*	<<<<<<<<	Podaci jezgre (vecina)		<<<<<<<<	      */
/******************************************************************************/

//interne funkcije samo za jezgra/dretve.c
static void j_stavi_u_pripravne (j_dretva_t *opd);
int j_stvori_dretvu_i (void *param, bool dretva_sustava);
void j_latentna_dretva (void *x); //za testiranje jezgrine dretve u abs. adr.

void j_visedretvenost_inicijaliziraj ()
{
	int i;

	for (i = 0; i < BRRAZPRI; i++) {
		j_pripravne_dretve[i].prvi = NULL;
		j_pripravne_dretve[i].zadnji = NULL;
	}

	j_redovi_inicijaliziraj();
}

//pomocne funkcije
void j_samo_stavi_u_pripravne (void *_opd)
{
	j_dretva_t *opd = _opd;

	DODAJ_NA_KRAJ(&j_pripravne_dretve[opd->prio], opd);
	opd->red = PRIPRAVNA;
	OZNACI_RED(j_pripravne_dretve, opd->prio);
}

static void j_stavi_u_pripravne (j_dretva_t *opd)
{
	j_samo_stavi_u_pripravne(opd);

	j_aktiviraj_prvu_pripravnu();
}

void j_aktiviraj_prvu_pripravnu ()
{
	int prio = PRVI_RED(j_pripravne_dretve);

	//aktivna se mijenja?
	if (j_aktivna == NULL || j_aktivna->red != AKTIVNA ||
			(prio != -1 && j_aktivna->prio > prio))
	{
		if (prio == -1 || j_pripravne_dretve[prio].prvi == NULL) {
			//j_ispisi("prio=%d\n", prio);
			j_zaustavi_potpuno("Red pripravnih prazan!\n");
		}

		//stavi trenutno aktivnu u pripravne (jer ce sad druga bit akt.)
		if (j_aktivna != NULL && j_aktivna->red == AKTIVNA)
			j_samo_stavi_u_pripravne(j_aktivna);

		j_aktivna = j_pripravne_dretve[prio].prvi;

		j_pripravne_dretve[prio].prvi = j_aktivna->iduci;

		if (j_pripravne_dretve[prio].prvi == NULL)
			OBRISI_RED(j_pripravne_dretve, prio);

		j_aktivna->iduci = NULL;
		arh_pripravi_okolinu_dretvi ( &j_aktivna->kontekst );
		j_aktivna->red = AKTIVNA;
	}
}


//stvaranje nove dretve - poziva se iz prekida
int j_stvori_dretvu (void *param)
{
	int id_procesa = j_aktivna->id_procesa;

	return j_stvori_dretvu_i(param, id_procesa);
}

//stvaranje nove dretve - poziva se iz jezgre
int j_stvori_dretvu_i (void *param, int id_procesa)
{
	struct {
		void (*poc_funkc)(void *);	//pocetna funkcija dreve
						//mora biti adresno prilagodjena

		void *param;			//parametar za stog
		void *stog;			//pocetni stog
		int prio;			//prioritet
		int rasp;			//koji rasporedjivac? FIFO, RR
	} *pd = param;

	int i, povr_vr = 0;
	j_dretva_t *opd;
	void *stog;

	PARAMNULL(pd);

	if (pd->poc_funkc == NULL) {
		j_ispisi("poc_funkc je NULL!\n");
		return -1;
	}

	i = DOHVATI ( j_dretva );

        if ( i != -1 ) {
		opd = &j_dretva[i];
		opd->id = i;
		opd->id_procesa = id_procesa;
		stog = pd->stog;
		opd->iduci = NULL;
		opd->prio = pd->prio;
		opd->rasp = pd->rasp;
		opd->red = -1;
		opd->alarm = -1;
		opd->kraj = -1;
		opd->povvr = NULL;
		j_dog_inic_podatke_dretve ( &opd->red_dog );

		//j_ispisi("stog=%x\n", stog);
		if ( stog == NULL ) //postavi stog
			stog = POCETAK_STOGA ( j_stog_dretve[i],
					       VELICINA_STOGA );

		//j_ispisi("stog=%x pokreni_dretvu=%x poc_funkc=%x param=%x\n",
		//	stog, pokreni_dretvu, pd->poc_funkc, pd->param);

		//postavi kazaljku stoga, a stog sredi tako da se
		//povratkom iz prekida vraca/pocinje s ovom dretvom
		j_proces_postavi_dretvu ( id_procesa,
					  opd,
					  &opd->kontekst,
					  stog,
					  pokreni_dretvu,
					  pd->poc_funkc,
					  pd->param );

		//dodaj dretvu u pripravne
		j_stavi_u_pripravne ( opd );

		//povratna vrijednost jezgrine funkcije - id dretve
		povr_vr = i;

	} else {
		j_ispisi ( "Nema slobodna opisnika za dretvu" );
		povr_vr = -1;
	}

	return povr_vr;
}


//'jezgrina funkcija', poziva se iz prekida
//daj procesor iducoj dretvi iz reda pripravnih
int j_dretva_odstupi (void *param)
{
	j_dretva_t *akt = j_aktivna;

	j_stavi_u_pripravne(j_aktivna);

	if (akt != j_aktivna)
		return 0; //dogodila se promjena dretve
	else
		return 1; //nije se dogodila promjena dretve
}

//kraj rada aktivne dretve, makni je iz reda aktivne
int j_dretva_zavrsetak (void *param)
{
	j_dretva_t *opd = j_aktivna;

	//j_obrisi_dretvu(j_aktivna);
	opd->red = PASIVNA;

	//ima dretvi koje cekaju na kraj ove?
	if (opd->kraj != -1) {
		j_obrisi_red(opd->kraj); //pri brisanju reda se odblokiraju sve
					  //dretve koje cekaju u redu
		opd->kraj = -1;
	}

	j_makni_dretvu_procesu ( opd->id_procesa, opd );

	OBRISI(j_dretva, opd->id);

	j_aktiviraj_prvu_pripravnu();

	return 0;
}

//zavrsi neku drugu dretvu
int j_zavrsi_dretvu ( void *param )
{
	int *pid = param, id = 0;

	if ( pid == NULL || ( id = *pid ) == -1 || id < 0 || id >= NAJ_DRETVI
		|| PROVJERI_SLOBODNO ( j_dretva, id ) )
	{
		j_ispisi("Dretva ne postoji (%d)!\n", id);
		return -1;
	}

	j_dretva_t *opd = &j_dretva[id];

	if ( opd->red == PRIPRAVNA ) {
		MAKNI_IZ_LISTE(	&j_pripravne_dretve[opd->prio], opd );

		if ( j_pripravne_dretve[opd->prio].prvi == NULL )
			OBRISI_RED ( j_pripravne_dretve, opd->prio );
	}
	else if ( opd->red != AKTIVNA && opd->red != PASIVNA ) {
		j_makni_iz_reda ( opd->red, opd );
	}
	opd->red = PASIVNA;

	//ima dretvi koje cekaju na kraj ove?
	if (opd->kraj != -1) {
		j_obrisi_red(opd->kraj); //pri brisanju reda se odblokiraju sve
					  //dretve koje cekaju u redu
		opd->kraj = -1;
	}

	if ( opd->id_procesa != -1)
		j_makni_dretvu_procesu ( opd->id_procesa, opd );

	OBRISI(j_dretva, opd->id);

	j_aktiviraj_prvu_pripravnu();

	return 0;
}

//prioriteti
int j_dohvati_prioritet (void *param)
{
	int *pid = param, id, prio = -1;

	if (pid == NULL || (id = *pid) == -1)
		id = j_aktivna->id;

	if (id >= 0 && id < NAJ_DRETVI && !PROVJERI_SLOBODNO(j_dretva, id) )
		prio = j_dretva[id].prio;

	return prio;
}

//nacin rasporedjivanja
int j_dohvati_nacin_rasporedjivanja (void *param)
{
	int *pid = param, id, prio = -1;

	if ((pid == NULL) || (id = *pid) == -1)
		id = j_aktivna->id;
	else
		id = *pid;

	if (id >= 0 && id < NAJ_DRETVI && !PROVJERI_SLOBODNO(j_dretva, id) )
		prio = j_dretva[id].rasp;

	return prio;
}

//promijeni prioritet aktivnoj dretvi (ona poziva ovu funkciju)
int j_postavi_prioritet (void *param)
{
	int *pprio = param, prio, sprio = j_aktivna->prio;

	PARAMNULL(pprio);

	prio = *pprio;

	if (prio < PRIO_MAX || prio > PRIO_MIN) {
		j_ispisi("Prioritet izvan dozvoljenih granica!\n");
		return -1;
	}

	j_aktivna->prio = prio;

	if (prio > sprio) //smanjen prioritet, mozda je sad neka druga prije ove
		j_aktiviraj_prvu_pripravnu();

	return sprio;
}

//dinamicko povecavanje/vracanje prioriteta

//interna funkcija - ne provjeravam parametre
//promijeni prioritet dretvi 'id'
int j_povecaj_prioritet (int id_dretve, int nprio, void *razlog)
{
	j_dretva_t *opd = &j_dretva[id_dretve];

	if (j_povecaj_din_prio(&opd->dp, opd->prio, nprio, razlog))
		return 1;

	return j_promijeni_prioritet(id_dretve, nprio);
}

int j_vrati_prioritet (int id_dretve, void *razlog)
{
	j_dretva_t *opd = &j_dretva[id_dretve];
	int nprio;

	nprio = j_vrati_din_prio(&opd->dp, razlog);

	if (nprio == -1)
		return 1;

	return j_promijeni_prioritet(id_dretve, nprio);
}

int j_promijeni_prioritet (int id_dretve, int nprio)
{
	j_dretva_t *opd = &j_dretva[id_dretve];

	if (opd->red == AKTIVNA) { //nije u redu, samo promjeni prioritet
		opd->prio = nprio;
	}
	else if (opd->red == PRIPRAVNA) {
		//makni iz trenutne razine
		MAKNI_IZ_LISTE(	&j_pripravne_dretve[opd->prio], opd);
		if (j_pripravne_dretve[opd->prio].prvi == NULL)
			OBRISI_RED(j_pripravne_dretve, opd->prio);

		//promijeni prioritet
		opd->prio = nprio;

		//dodaj u novu razinu
		DODAJ_NA_KRAJ(&j_pripravne_dretve[nprio], opd);
		OZNACI_RED(j_pripravne_dretve, nprio);
	}
	else if (opd->red != PASIVNA) { //negdje ceka
		j_makni_iz_reda(opd->red, opd);

		opd->prio = nprio;

		j_stavi_u_red(opd->red, opd, NULL);
	}

	j_aktiviraj_prvu_pripravnu(); //azuriraj aktivnu - mozda se promijenila

	return 0;
}

int j_cekaj_kraj_dretve (void *param) //aktivna dretva zove
{
	int *pid = param, id;

	PARAMNULL(pid);
	id = *pid;

	if (id >= 0 && id < NAJ_DRETVI) {
		if ( PROVJERI_SLOBODNO ( j_dretva, id ) )
			return 1; //dretva je vec prije zavrsila ili ne postoji

		if ( j_dretva[id].kraj == -1 ) {
			j_dretva[id].kraj = j_dohvati_red ( REDDRETVE, SORT_PRP,
							    0 );
			if ( j_dretva[id].kraj == -1 )
				return -1;
		}

		j_stavi_u_red(j_dretva[id].kraj, j_aktivna, NULL);

		j_aktiviraj_prvu_pripravnu();

		return 0;
	}

	return -1;
}

/* - ne koristi se
int j_zaustavi_latentnu_dretvu (void *param)
{
	while(1) {
		j_dozvoli_prekidanje();
		j_zaustavi();
	}

	return 0;
}
*/

//za vremenski ograniceno blokiranje dretve
void j_postavi_dretvin_alarm (void *_opd, void *_koliko)
{
	j_dretva_t *opd = _opd;
	vrijeme_t *koliko = _koliko;
	alarm_t t;

	if (opd == NULL || PROVJERI_SLOBODNO(j_dretva, opd->id) ||
		koliko == NULL || (koliko->sekunde | koliko->nsekunde) == 0)
		return;

	t.id = 0;
	t.kada = *koliko;
	t.obrada = NULL;
	t.param = opd;
	t.zast = STVORINOVI | OSLOBODIDR;

	opd->alarm = j_alarm_dohvati(&t);
}
void j_obrisi_dretvin_alarm (void *_opd)
{
	j_dretva_t *opd = _opd;

	if (opd == NULL || PROVJERI_SLOBODNO(j_dretva, opd->id) ||
							opd->alarm == -1)
		return;

	j_alarm_obrisi(&opd->alarm);
	opd->alarm = -1;
}

//"nasilno" odblokiranje dretve iz reda (npr. istekao je alarm za cekanje)
void j_odblokiraj_dretvu (void *_opd)
{
	j_dretva_t *opd = _opd;

	if (opd == NULL || PROVJERI_SLOBODNO(j_dretva, opd->id) ||
		opd->red == AKTIVNA || opd->red == PRIPRAVNA)
		return;

	j_makni_iz_reda (opd->red, opd);
	j_stavi_u_pripravne (opd);

	if (opd->alarm != -1) {
		j_alarm_obrisi(&opd->alarm);
		opd->alarm = -1;
	}
	if (opd->povvr != NULL) {
		*opd->povvr = -2; //promijeni povratnu vrijednost
		opd->povvr = NULL;//obrisi referencu na pov. vr.
	}
}

void j_zapisi_adresu_povratne_vrijednosti(int *ppovvr)
{
	j_aktivna->povvr = ppovvr;
}

//aktivira se kad nema nista drugo - radi u jezgrinom nacinu rada,
//u jezgrinom adresom prostoru (od adrese 0 do 4GB, stvarne adrese)
void j_latentna_dretva (void *x)
{
	while(1) {
		j_dozvoli_prekidanje();
		j_zaustavi();
	}
}

//dohvacanje elemenata dretvi - interne "jezgrine" funkcije

inline void *j_aktivna_dretva()
{
	return j_aktivna;
}

inline void *j_dohvati_opisnik (int id)
{
	if (id == -1 || id >= NAJ_DRETVI || PROVJERI_SLOBODNO(j_dretva, id))
		return NULL;
	else
		return &j_dretva[id];
}

inline void *j_dohvati_kontekst_aktivne ()
{
	return &j_aktivna->kontekst;
}

inline void *j_opdr_kontekst(void *opd)
{
	return &((j_dretva_t *) opd)->kontekst;
}

//vraca id trenutne dretve (j-funkcija)
int j_id_dretve (void *param)
{
	return j_aktivna->id;
}

inline int j_opdr_id(void *opd)
{
	return ((j_dretva_t *) opd)->id;
}

inline int j_aktivni_proces()
{
	return j_aktivna->id_procesa;
}

inline int j_opdr_id_procesa (void *opd)
{
	return ((j_dretva_t *) opd)->id_procesa;
}

inline int j_opdr_prio(void *opd)
{
	return ((j_dretva_t *) opd)->prio;
}

inline int j_opdr_red(void *opd)
{
	return ((j_dretva_t *) opd)->red;
}

inline void j_opdr_postavi_red(void *opd, int red)
{
	((j_dretva_t *) opd)->red = red;
}

inline void *j_opdr_red_dog(void *opd)
{
	return &((j_dretva_t *) opd)->red_dog;
}


